package broker

import (
	"fmt"
	"github.com/golang/protobuf/proto"
	"github.com/micro/go-micro/v2/broker"
	"github.com/micro/go-micro/v2/broker/nats"
	"strconv"
	"strings"

	"kop/modules/api/models"
	"kop/pb"
	"kop/util/log"
)

var Broker = nats.NewBroker()

func Init(addr string) error {

	if addr == "" {
		addr = "127.0.0.1:4222"
	}

	if err := Broker.Init(broker.Addrs(strings.Split(addr, ",")...)); err != nil {
		return err
	}

	return Broker.Connect()
}

type Signal uint8

const (
	// 服务器自用消息
	JoinGuild    Signal = 211 //  加入公会
	QuitGuild    Signal = 212 //  离开公会
	JoinRoom     Signal = 221 //  加入房间
	JoinActivity Signal = 222 //  加入活动

)

func NewSubscribers() map[string]broker.Subscriber {

	return make(map[string]broker.Subscriber)
}

type Subscribers map[string]broker.Subscriber

func (s *Subscribers) Subscribe(topic string, c chan<- []byte) error {

	if _, ok := (*s)[topic]; ok {
		return nil
	}

	var subscriber, err = Subscribe(topic, c)
	if err == nil {
		(*s)[topic] = subscriber
	}
	return err
}

func (s *Subscribers) Unsubscribe() {

	for _, v := range *s {
		_ = v.Unsubscribe()
	}
}

// 退订某一类的主题
func (s *Subscribers) UnContains(topic string) {

	for _, v := range *s {
		if strings.Contains(v.Topic(), topic) {
			_ = v.Unsubscribe()

			delete(*s, v.Topic())
		}
	}
	//*s = newSubs
}

//socket 个人频道特殊处理
func SubRoleChannel(token string, user *pb.User, c chan<- []byte, subscribers *Subscribers) (broker.Subscriber, error) {

	// 订阅个人消息
	return Broker.Subscribe(fmt.Sprintf("go.micro.topic.role%d", user.RoleID), func(e broker.Event) error {

		//log.Info("RoleID[%d], 收到个人消息, %s, %s", user.RoleID, e.Message().Header["Code"], e.Message().Body)

		var Code, err = strconv.Atoi(e.Message().Header["Code"])
		if err != nil {
			return err
		}

		switch Signal(Code) {

		case JoinGuild:

			go func() {

				var s, err = strconv.Atoi(e.Message().Header["ID"])
				if err != nil {
					return
				}

				user.GuildID = int32(s)
				_ = models.SaveUser(token, user)

				c <- append([]byte{uint8(JoinGuild)})

				_ = subscribers.Subscribe("go.micro.topic.guild"+e.Message().Header["ID"], c)
			}()

		case QuitGuild:

			go func() {

				subscribers.UnContains("go.micro.topic.guild")

				user.GuildID = 0
				_ = models.SaveUser(token, user)
			}()

		default:

			log.Info("RoleID%d, Code=%d 发送到客户端", user.RoleID, Code)

			c <- append([]byte{uint8(Code)}, e.Message().Body...)

		}

		return nil
	})
}

func Server0Publish(code pb.WsCode, message proto.Message) error {

	return publish("go.micro.topic.server0", code, message)
}

// 发送个人频道消息
func RolePublish(roleID int32, code pb.WsCode, message proto.Message) error {

	return publish(fmt.Sprintf("go.micro.topic.role%d", roleID), code, message)
}

// 发送本服频道消息
func ServerPublish(serverID int32, code pb.WsCode, message proto.Message) error {

	return publish(fmt.Sprintf("go.micro.topic.server%d", serverID), code, message)
}

// 发送公会频道消息
func GuildPublish(guildID int32, code pb.WsCode, message proto.Message) error {

	return publish(fmt.Sprintf("go.micro.topic.guild%d", guildID), code, message)
}

func RoomPublish(name string, serverID int32, code pb.WsCode, message proto.Message) error {

	return publish(fmt.Sprintf("go.micro.topic.%s.%d", name, serverID), code, message)
}

//// 发送世界BOSS血量
//func WorldBossPublish(serverID int32, HP int64) error {
//
//	return publish(fmt.Sprintf("go.micro.topic.world_boss.%d", serverID), pb.WsCode_WorldBossHP, &wrappers.Int64Value{Value: HP})
//}

// 订阅频道
// 公会|活动|其它等等ID
func SubSubject(roleID int32, code Signal, ID int32) error {

	var m = broker.Message{
		Header: map[string]string{
			"Code": strconv.Itoa(int(code)),
			"ID":   strconv.Itoa(int(ID)),
		},
	}

	return Broker.Publish(fmt.Sprintf("go.micro.topic.role%d", roleID), &m)
}

func Subscribe(topic string, c chan<- []byte) (broker.Subscriber, error) {

	return Broker.Subscribe(topic, func(e broker.Event) error {

		var Code, err = strconv.Atoi(e.Message().Header["Code"])
		if err != nil {
			return err
		}

		//log.Info("收到消息 Code=%d, Message=%s ", Code, e.Message().Body)

		c <- append([]byte{uint8(Code)}, e.Message().Body...)
		return nil
	})
}

func publish(topic string, code pb.WsCode, message proto.Message) error {

	var m = broker.Message{
		Header: map[string]string{"Code": strconv.Itoa(int(code))},
	}

	m.Body, _ = proto.Marshal(message)

	return Broker.Publish(topic, &m)
}
